package simbot.example.utils.client;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Http连接池
 *
 * @author minmin 谢钱枫
 * @since 2021 /07/10 11:07
 */
@Slf4j
public class HttpClientUtil {
    private HttpClientUtil() {
    }

    private static final PoolingHttpClientConnectionManager CM;

    private static final IdleConnectionThread IDLE_CONNECTION_THREAD;

    static {
        CM = new PoolingHttpClientConnectionManager();
        CM.setMaxTotal(100);
        CM.setDefaultMaxPerRoute(20);
        // 启动该线程，负责清理无效连接
        IDLE_CONNECTION_THREAD = new IdleConnectionThread(CM);
        IDLE_CONNECTION_THREAD.start();
    }

    /**
     * The type Idle connection thread.
     */
    public static class IdleConnectionThread extends Thread {

        private final HttpClientConnectionManager connMgr;

        /**
         * Instantiates a new Idle connection thread.
         *
         * @param connMgr the conn mgr
         */
        public IdleConnectionThread(HttpClientConnectionManager connMgr) {
            this.connMgr = connMgr;
        }

        @Override
        public void run() {
            try {
                while (!isInterrupted()) {
                    synchronized (this) {
                        wait(5000);
                        // 关闭失效的连接
                        connMgr.closeExpiredConnections();
                    }
                }
            } catch (InterruptedException ex) {
                // 结束
            }
        }
    }

    /**
     * Send get string.
     *
     * @param url the url
     * @return the string
     */
    public static String sendGet(String url) {
        return sendGet(url, null, null);
    }

    /**
     * Send get use proxy string.
     *
     * @param url      the url
     * @param httpHost the http host
     * @return the string
     */
    public static String sendGetUseProxy(String url, HttpHost httpHost) {
        return sendGet(url, null, null);
    }

    /**
     * Send get string.
     *
     * @param url       the url
     * @param headerMap the header map
     * @return the string
     */
    public static String sendGet(String url, Map<String, String> headerMap) {
        return sendGet(url, headerMap, null);
    }

    /**
     * Send get string.
     *
     * @param url       the url
     * @param headerMap the header map
     * @param proxy     the proxy
     * @return the string
     */
    public static String sendGet(String url, Map<String, String> headerMap, HttpHost proxy) {
        CloseableHttpClient httpclient = getCloseableHttpClient(proxy);
        HttpGet httpGet = new HttpGet(url);
        if (headerMap != null) {
            for (String key : headerMap.keySet()) {
                httpGet.setHeader(key, headerMap.get(key));
            }
        } else {
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
        }
        return sendRequest(httpclient, httpGet);
    }

    /**
     * Send post string.
     *
     * @param url      the url
     * @param paramMap the param map
     * @return the string
     */
    public static String sendPost(String url, Map<String, Object> paramMap) {
        return sendPost(url, paramMap, null);
    }

    /**
     * Send post string.
     *
     * @param url       the url
     * @param paramMap  the param map
     * @param headerMap the header map
     * @return the string
     */
    public static String sendPost(String url, Map<String, Object> paramMap, Map<String, String> headerMap) {
        return sendPost(url, paramMap, headerMap, null);
    }

    /**
     * Send post string.
     *
     * @param url       the url
     * @param content   the content
     * @param headerMap the header map
     * @param proxy     the proxy
     * @return the string
     */
    public static String sendPost(String url, String content, Map<String, String> headerMap, HttpHost proxy) {
        CloseableHttpClient httpclient = getCloseableHttpClient(proxy);
        HttpPost httpPost = new HttpPost(url);
        if (headerMap != null) {
            for (String key : headerMap.keySet()) {
                httpPost.setHeader(key, headerMap.get(key));
            }
        }
        try {
            StringEntity entity = new StringEntity(content);
            httpPost.setEntity(entity);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sendRequest(httpclient, httpPost);
    }

    /**
     * Send post string.
     *
     * @param url       the url
     * @param paramMap  the param map
     * @param headerMap the header map
     * @param proxy     the proxy
     * @return the string
     */
    public static String sendPost(String url, Map<String, Object> paramMap, Map<String, String> headerMap, HttpHost proxy) {
        CloseableHttpClient httpclient = getCloseableHttpClient(proxy);
        HttpPost httpPost = new HttpPost(url);
        if (headerMap != null) {
            for (String key : headerMap.keySet()) {
                httpPost.setHeader(key, headerMap.get(key));
            }
        } else {
            httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
        }
        return getResponseStr(paramMap, httpclient, httpPost);
    }

    private static CloseableHttpClient getCloseableHttpClient() {
        return getCloseableHttpClient(null);
    }

    private static CloseableHttpClient getCloseableHttpClient(HttpHost proxy) {
        return proxy == null
                ? HttpClients.custom()
                .setConnectionManager(CM)
                .build()
                : HttpClients.custom()
                .setConnectionManager(CM)
                .setProxy(proxy)
                .build();
    }

    private static String getResponseStr(Map<String, Object> paramMap, CloseableHttpClient httpclient, HttpPost httpPost) {
        if (paramMap != null && !paramMap.isEmpty()) {
            List<NameValuePair> nameValuePairs = new ArrayList<>();
            for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs, Consts.UTF_8);
            httpPost.setEntity(entity);
        }
        return sendRequest(httpclient, httpPost);
    }

    /**
     * Send post string.
     *
     * @param url     the url
     * @param content the content
     * @param proxy   the proxy
     * @return the string
     */
    public static String sendPost(String url, String content, HttpHost proxy) {
        CloseableHttpClient httpclient = getCloseableHttpClient(proxy);
        HttpPost httpPost = new HttpPost(url);
        try {
            StringEntity entity = new StringEntity(content);
            httpPost.setEntity(entity);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sendRequest(httpclient, httpPost);
    }

    /**
     * Do post json string.
     *
     * @param url         the url
     * @param json        the json
     * @param tokenHeader the token header
     * @return the string
     * @throws Exception the exception
     */
    public static String doPostJson(String url, String json, String tokenHeader) throws Exception {
        CloseableHttpClient httpClient = getCloseableHttpClient();
        HttpPost httpPost = getHttpPost(url, json, tokenHeader);
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                Header encoding = response.getEntity().getContentEncoding();
                if (encoding != null) {
                    Charset defaultCharset = Charset.forName(encoding.getName());
                    return EntityUtils.toString(response.getEntity(), defaultCharset);
                }
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        }
        return null;
    }

    /**
     * Do get download file boolean.
     *
     * @param savePath  the save path
     * @param fileName  the file name
     * @param url       the url
     * @param headerMap the header map
     * @param proxy     the proxy
     * @return the boolean
     * @throws IOException the io exception
     */
    public static String doGetDownloadFile(String savePath, String fileName, String url, Map<String, String> headerMap, HttpHost proxy) throws IOException {
        if (StringUtils.isEmpty(url)) {
            throw new RuntimeException("url is null!");
        }
        Path path = Paths.get(savePath, fileName);
        if (Files.isDirectory(path)) {
            throw new RuntimeException("full path is directory!");
        }
        if (Files.notExists(path.getParent())) {
            Files.createDirectories(path.getParent());
        }
        CloseableHttpClient httpClient = getCloseableHttpClient(proxy);
        HttpGet httpGet = new HttpGet(url);
        if (headerMap != null) {
            for (String key : headerMap.keySet()) {
                httpGet.setHeader(key, headerMap.get(key));
            }
        } else {
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
        }
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            HttpEntity responseEntity = response.getEntity();
            try (InputStream responseEntityContent = responseEntity.getContent();
                 FileOutputStream fileOutputStream = new FileOutputStream(path.toString())) {
                int pass;
                while ((pass = responseEntityContent.read()) != -1) {
                    fileOutputStream.write(pass);
                }
                fileOutputStream.flush();
                return path.toString();
            }
        }
    }

    /**
     * Gets http post.
     *
     * @param url         the url
     * @param json        the json
     * @param tokenHeader the token header
     * @return the http post
     * @throws UnsupportedEncodingException the unsupported encoding exception
     */
    public static HttpPost getHttpPost(String url, String json, String tokenHeader) throws UnsupportedEncodingException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("HTTP Method", "POST");
        httpPost.setHeader("Connection", "Keep-Alive");
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        httpPost.setHeader("x-authentication-token", tokenHeader);
        StringEntity entity = new StringEntity(json);
        entity.setContentType("application/json;charset=utf-8");
        httpPost.setEntity(entity);
        return httpPost;
    }

    private static String sendRequest(CloseableHttpClient httpclient, HttpRequestBase request) {
        // 设置请求配置
        request.setConfig(getRequestConfig());
        try (CloseableHttpResponse response = httpclient.execute(request)) {
            if (response.getStatusLine().getStatusCode() == 200) {
                HttpEntity entity = response.getEntity();
                Header encoding = response.getEntity().getContentEncoding();
                if (encoding != null) {
                    Charset defaultCharset = Charset.forName(encoding.getName());
                    return EntityUtils.toString(response.getEntity(), defaultCharset);
                }
                return EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }


    private static RequestConfig getRequestConfig() {
        return RequestConfig.custom()
                // 创建连接的最长时间
                .setConnectTimeout(1000)
                // 从连接池获取连接的最长时间
                .setConnectionRequestTimeout(500)
                // 数据传输最长时间
                .setSocketTimeout(10000)
                .build();
    }

    /**
     * Close.
     */
    public static void close() {
        log.debug("HttpClientUtil closing...");
        IDLE_CONNECTION_THREAD.interrupt();
        CM.close();
        log.debug("HttpClientUtil close complete...");
    }
}
